// 把 $data 选项中的属性转换成响应式(get/set)的数据
class Observer {
  constructor(data) {
    this.walk(data);
  }

  walk(data) {
    // 1. 判断 data 是否为对象
    if (!data || typeof data !== "object") {
      return;
    }
    // 2. 遍历 data 对象的所有属性
    Object.keys(data).forEach((key) => {
      this.defineReactive(data, key, data[key]);
    });
  }

  defineReactive(obj, key, val) {
    // 递归转换成响应式数据
    const that = this;
    // 收集依赖并发送通知
    let dep = new Dep();
    // 递归, 如果 val 是对象, 就将 val 的属性也转换成响应式数据
    this.walk(val);
    Object.defineProperty(obj, key, {
      enumerable: true,
      configurable: true,
      get() {
        // ✨ 这一步很重要, 为每个属性创建观察者Dep
        // 一个属性 →
        // 对应一个观察者 Dep (收集 watch, 通知 watch 进行变更) →
        // 对应多个 Watch (页面上有几个地方用到这个属性就会有几个watch, 所以在 compile 的时候创建 watch)
        Dep.target && dep.addSub(Dep.target);

        // 之所以用 val, 而不是用 obj[key],
        // 是因为如果使用 obj[key], 会再次触发 get 方法, 造成堆栈溢出
        return val;
      },
      set(newValue) {
        if (newValue === val) {
          return;
        }
        val = newValue;
        that.walk(newValue);
        // 数据改变的时候通知引用的数据的视图进行更新
        dep.notify();
      },
    });
  }
}
